#define LOG_TAG    "socket_client"

#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdint.h>
#include <pthread.h>

#include <sys/stat.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>

#include <sys/param.h>
#include <sys/types.h> 
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <errno.h>

#include "project_common.h"


#define CLIENT_MSG_TYPE         10
#define CLIENT_MSG_DATA_SIZE    2*1024
typedef struct {
    long     mtype;
    uint8_t  data[CLIENT_MSG_DATA_SIZE];
} client_send_q_t;

#define INVALID_SOCKET          (-1)
static volatile int sock = INVALID_SOCKET;
static volatile struct sockaddr_in dest_addr;

static volatile int client_send_queue;

//判断是否为透传模式的回调函数
bool (*app_socket_check_is_pass)(void) = NULL;
//设置进入透传模式的回调函数
void (*app_socket_set_to_pass)(void) = NULL;
static bool app_socket_client_reconnect(const struct sockaddr_in *paddr);

// 通过socket client发送数据到server
void app_socket_client_send_data(uint8_t *data, uint16_t len)
{
    client_send_q_t send_buf;
    if (len > CLIENT_MSG_DATA_SIZE) {
        len = CLIENT_MSG_DATA_SIZE;
    }
    memcpy(send_buf.data, data, len);
    send_buf.mtype = CLIENT_MSG_TYPE;
    if (msgsnd(client_send_queue, &send_buf, len, 0) == -1) {
        log_e("msgsnd fail %d -> %s\n", errno, strerror(errno));
        exit(-1);
    }
}

//socket client 接收任务
static void *app_socket_client_recv_task(void *arg)
{
    static uint8_t rx_buffer[CLIENT_MSG_DATA_SIZE];
    log_i("app_socket_client_recv_task");
    while (1)
    {
        int len = recv(sock, rx_buffer, sizeof(rx_buffer) - 1, 0);
        // Error occurred during receiving
        if (len <= 0)
        {
            if (len < 0) {
                log_e("recv failed: errno %d", errno);
            }
            if ( ((errno==ENOTCONN || errno==ECONNRESET)&&(len < 0)) || (len == 0) )
            {
                while (1) {//循环重连
                    if (!app_socket_client_reconnect((struct sockaddr_in*)&dest_addr)) {
                        usleep(500*1000);//500ms
                    }
                    else {
                        break;
                    }
                }
            }
        }
        else {
            log_i("Received %d bytes from %s:", len, inet_ntoa(dest_addr.sin_addr));

            // 在透传模式下，接受到的数据传送到串口
            if (app_socket_check_is_pass != NULL)
            {
                if (app_socket_check_is_pass()) {
                    USER_UART_SEND(rx_buffer, len);
                }
            }
        }
    }
    pthread_exit(NULL);
}

//socket client 发送任务
static void *app_socket_client_send_task(void *arg)
{
    static client_send_q_t send_buffer;
    log_i("app_socket_client_send_task");
    while (1)
    {
        //等待需要发送的数据
        ssize_t rlen = msgrcv(client_send_queue, &send_buffer, sizeof(send_buffer)-sizeof(send_buffer.mtype), CLIENT_MSG_TYPE, 0);

        if (rlen > 0)
        {
            int len = send(sock, send_buffer.data, rlen, 0);

            if (len <= 0)
            {
                if (len < 0) {
                    log_e("Error occurred during sending: errno %d", errno);
                }
                if ( ((errno==ENOTCONN || errno==ECONNRESET)&&(len < 0)) || (len == 0) )
                {
                    while (1) {//循环重连
                        if (!app_socket_client_reconnect((struct sockaddr_in*)&dest_addr)) {
                            usleep(500*1000);//500ms
                        }
                        else {
                            break;
                        }
                    }
                }
            }
        }
    }
    pthread_exit(NULL);
}

/**
* @brief socket的重连
* @param[in] paddr:服务器相关信息
* @retval true:成功 false:失败
*/
static bool app_socket_client_reconnect(const struct sockaddr_in *paddr)
{
    pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER;

    if (paddr == NULL) {
        return false;
    }

    pthread_mutex_lock(&mtx);

    if (sock != INVALID_SOCKET)
    {
        close(sock);
        sock = INVALID_SOCKET;
    }
    sock =  socket(AF_INET, SOCK_STREAM, 0);
    int err = connect(sock, (struct sockaddr *)paddr, sizeof(struct sockaddr));

    log_i("sock=%d,addr=%x,port=%x", sock, (int)paddr->sin_addr.s_addr, paddr->sin_port);
    if (err != 0) {
        log_e("Socket unable to reconnect: errno %d -> %s\n", errno, strerror(errno));
        close(sock);
        sock = INVALID_SOCKET;
        USER_UART_INFO("[ATPC] ERROR:3\n");//连接 server失败

        pthread_mutex_unlock(&mtx);
        return false;
    }
    log_i("Successfully connected");

    pthread_mutex_unlock(&mtx);
    return true;
}

// ATPC指令对应处理函数
void app_socket_client_connect(int argc, char** argv)
{
    if (argv[0] != NULL) {//打印命令描述
        log_i("%s", argv[0]);
    }
    if ( (argv[1]==NULL)||(argv[2]==NULL)||(argv[3]==NULL) )
    {
        USER_UART_INFO("[ATPC] ERROR:2\n");//参数错误
        return;
    }
    //是否连接上ap了
    if ( !app_wifi_is_connect_ap() ) {
        USER_UART_INFO("[ATPC] ERROR:4\n");//尚未连接AP
        return;
    }

    char *ip_addr=argv[2];
    char *ip_port=argv[3];

    static bool is_inited = false;
    if (is_inited) {//当前版本只允许创建一个client
        log_e("Socket is inited");
        return;
    }

    //初始化服务器信息，并发起首次连接
    dest_addr.sin_addr.s_addr = inet_addr((char*)ip_addr);
    dest_addr.sin_family = AF_INET;
    dest_addr.sin_port = htons((uint16_t)atoi((char*)ip_port));
    if (false == app_socket_client_reconnect((struct sockaddr_in*)&dest_addr)) {
        return;
    }
    is_inited = true;
    log_i("Successfully connected");
    USER_UART_INFO("[ATPC] OK\n");

    //create queue
    client_send_queue = msgget(IPC_PRIVATE, S_IRUSR | S_IWUSR);//0600
    if (client_send_queue == -1) {
        log_e("msgget fail = %s\n", strerror(errno));
        exit(-1);
    }

    //设置进入透传模式
    if (app_socket_set_to_pass != NULL) {
        app_socket_set_to_pass();
    }

    pthread_t tid;
    pthread_create(&tid, NULL, app_socket_client_recv_task, NULL);
    pthread_create(&tid, NULL, app_socket_client_send_task, NULL);
    return;
}

/**
* @brief 注册socket模块的回调函数
* @param[in] check_pass:判断是否为透传模式的回调函数
* @param[in] set_pass:设置进入透传模式的回调函数
*/
void app_socket_regist_cb(bool (*check_pass)(void), void (*set_pass)(void))
{
    app_socket_check_is_pass = check_pass;
    app_socket_set_to_pass   = set_pass;
}
